package com.tofacebook.proxy.daili;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@Slf4j
public class DynamisProxy {

    //动态生成代理类不太确定，需要在程序运行的时候去动态生成对应的代理类
    private Object proxyClass;

    /**
     * 需要传递一个目标类来构造我们的代理实现类
     * @param proxyClass
     */
    public DynamisProxy(Object proxyClass) {
        this.proxyClass = proxyClass;
    }
    /**
     * 实例化代理实现类;
     * 要通过上面技术去实现呢？？
     * 我们要使用jdk 里面Proxy通过反射的机制去构造我们代理类
     * ClassLoader loader, 类的加载器，要加载那个代理对象
     * Class<?>[] interfaces, 所有的接口存存放在数组，代理类需要实现接口中的方法，然后调用方法
     * InvocationHandler h //处理方式：说白了，就是要实现我们接口中的方法
     */
    public Object getDynamicProxy(){
        /**
         * 通过Object对象对应的运行时类的实例Class对象
         * 调用方法得到的系统类加载【应用加载器】
         *
         */
        ClassLoader loader = proxyClass.getClass().getClassLoader();
        Class<?>[] interfaces = proxyClass.getClass().getInterfaces();
        InvocationHandler h = getInvocationHandler();
        return Proxy.newProxyInstance(loader,interfaces,h);
    }

    private InvocationHandler getInvocationHandler() {
        /**
         *  1.proxy：代理对象
         *  2.method：代理对象需要实现的方法，即其中需要重写的方法
         *            method 这是通过反射的机制生成的方法对应的类
         *            method.invoke(参数1:是某一个对象，参数2:调用方法需要传递参数)
         *  3.args：method所对应方法的参数
         *  4.目标方法就是  EmpServiceImpl.queryEmpByEmpId(参数列表)
         */
        return new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                log.info("在调用目标方法之前，需要打印信息，这叫前置通知");
                Object result = null;
                try {
                    //动态的去调用目标中的方法，并传值，最后得到返回的结果集
                    result = method.invoke(proxyClass, args);
                    log.info("在调用目标方法之后，需要打印信息，这叫返回通知，得到结果：{}",result);
                } catch (Exception e) {
                    e.printStackTrace();
                    //程序出现异常了，需要做一些什么事情
                    log.info("在调用目标方法异常，异常通知");
                } finally {
                    //异常之后，程序会终止，
                    // 那么终止之前需要做些什么事情，可以在这个作用域写对对那个代码片段
                    log.info("在调用目标方法异常之后，在程序结束之前，后置通知");
                }
                return result;
            }
        };
    }


}
